package io.lvdaxian.mybatis10.type;

import java.lang.reflect.Type;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;

/**
 * 类型事件 注册的方法/ 类型处理器注册机
 *
 * @author lihh
 */
public class TypeHandlerRegistry {
  private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<>(JdbcType.class);
  private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<>();
  private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<>();
  
  public TypeHandlerRegistry() {
    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());
    
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
  }
  
  /**
   * 类型 注册
   *
   * @param javaType    对应 Java 类型
   * @param typeHandler 类型 执行事件
   * @param <T>         泛型
   * @author lihh
   */
  private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
    register(javaType, null, typeHandler);
  }
  
  /**
   * 重载类型
   *
   * @param javaType java 类型
   * @param jdbcType db 类型
   * @param handler  对应的事件
   * @author lihh
   */
  private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (null != javaType) {
      Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.computeIfAbsent(javaType, k -> new HashMap<>());
      map.put(jdbcType, handler);
    }
    ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
  }
  
  /**
   * 获取 参数设定方法
   *
   * @param type     java type
   * @param jdbcType db 类型
   * @param <T>      泛型
   * @return 返回一个 handler 事件
   * @author lihh
   */
  public <T> TypeHandler<T> getTypeHandler(Class<T> type, JdbcType jdbcType) {
    return getTypeHandler((Type) type, jdbcType);
  }
  
  public boolean hasTypeHandler(Class<?> javaType) {
    return hasTypeHandler(javaType, null);
  }
  
  public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
    return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
  }
  
  private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
    // jdbc type 集合
    Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
    TypeHandler<?> handler = null;
    if (jdbcHandlerMap != null) {
      handler = jdbcHandlerMap.get(jdbcType);
      if (handler == null) {
        handler = jdbcHandlerMap.get(null);
      }
    }
    // type drives generics here
    return (TypeHandler<T>) handler;
  }
}
